package com.example.mq.mqclient;

import com.example.mq.common.BinaryTool;
import com.example.mq.common.MqException;
import com.example.mq.common.network.BasicArguments;
import com.example.mq.common.network.BasicReturns;
import com.example.mq.common.network.Request;
import com.example.mq.common.network.expansion.*;
import com.example.mq.mqserver.core.BasicProperties;
import com.example.mq.mqserver.core.Consume;
import com.example.mq.mqserver.core.ExchangeType;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 这个类表示 一次tcp连接中的 channel
 */
public class Channel {
    //  channel 标识
    private String channelId;
    //  表示属于哪一个连接
    private Connection connection;
    //  引入map，存放客服端收到的响应
    //  key=rid，区分开请求，后续可以判断出请求是否已经响应
    private ConcurrentHashMap<String, BasicReturns> basicReturnsMap = new ConcurrentHashMap<>();
    //  记录回调
    private Consume consume;

    public Channel(String channelId,Connection connection) {
        this.channelId = channelId;
        this.connection = connection;
    }

    //  通知服务器，客户端创建了 channel
    public boolean createChannel() throws IOException {
        //  构造请求
        Request request = new Request();
        request.setType(0x1);
        BasicArguments arguments = new BasicArguments();
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(arguments);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待响应
        BasicReturns basicReturns = waitReturn(arguments.getRid());

        return basicReturns.isOk();
    }

    //  销毁 channel
    public boolean destroy() throws IOException {
        //  构造请求
        Request request = new Request();
        request.setType(0x2);
        BasicArguments basicArguments = new BasicArguments();
        basicArguments.setChannelId(channelId);
        basicArguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(basicArguments);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待响应
        BasicReturns basicReturns = waitReturn(basicArguments.getRid());
        return basicReturns.isOk();
    }

    //  创建交换机
    public boolean exchangeDeclare(String exchangeName, ExchangeType exchangeType, boolean durable,
                                   boolean autoDelete, Map<String,Object> arguments) throws IOException {
        //  构造请求
        ExchangeDeclareArguments exchangeDeclareArguments = new ExchangeDeclareArguments();
        exchangeDeclareArguments.setExchangeName(exchangeName);
        exchangeDeclareArguments.setExchangeType(exchangeType);
        exchangeDeclareArguments.setDurable(durable);
        exchangeDeclareArguments.setAutoDelete(autoDelete);
        exchangeDeclareArguments.setArguments(arguments);
        exchangeDeclareArguments.setChannelId(channelId);
        exchangeDeclareArguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(exchangeDeclareArguments);
        Request request = new Request();
        request.setType(0x3);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待响应
        BasicReturns basicReturns = waitReturn(exchangeDeclareArguments.getRid());

        return basicReturns.isOk();
    }

    //  销毁交换机
    public boolean exchangeDelete(String exchangeName) throws IOException {
        //  构造请求
        ExchangeDeleteArguments arguments = new ExchangeDeleteArguments();
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        arguments.setExchangeName(exchangeName);
        byte[] payload = BinaryTool.toBytes(arguments);
        Request request = new Request();
        request.setType(0x4);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待响应
        BasicReturns basicReturns = waitReturn(arguments.getRid());

        return basicReturns.isOk();
    }

    //  创建队列
    public boolean queueDeclare(String queueName,boolean durable,boolean autoDelete,boolean exclusive,Map<String,Object> argument) throws IOException {
        //  构造请求
        QueueDeclareArguments queueDeclareArguments = new QueueDeclareArguments();
        queueDeclareArguments.setQueueName(queueName);
        queueDeclareArguments.setDurable(durable);
        queueDeclareArguments.setAutoDelete(autoDelete);
        queueDeclareArguments.setExclusive(exclusive);
        queueDeclareArguments.setArguments(argument);
        queueDeclareArguments.setChannelId(channelId);
        queueDeclareArguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(queueDeclareArguments);
        Request request = new Request();
        request.setType(0x5);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待请求
        BasicReturns basicReturns = waitReturn(queueDeclareArguments.getRid());

        return basicReturns.isOk();
    }

    //  销毁队列
    public boolean queueDelete(String queueName) throws IOException {
        //  构造请求
        QueueDeleteArguments arguments = new QueueDeleteArguments();
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        arguments.setQueueName(queueName);
        byte[] payload = BinaryTool.toBytes(arguments);
        Request request = new Request();
        request.setType(0x6);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待响应
        BasicReturns basicReturns = waitReturn(arguments.getRid());

        return basicReturns.isOk();
    }

    //  创建绑定
    public boolean queueBind(String exchangeName,String queueName,String bindingKdy) throws IOException {
        //  构造请求
        QueueBindArguments arguments = new QueueBindArguments();
        arguments.setExchangeName(exchangeName);
        arguments.setQueueName(queueName);
        arguments.setBindingKey(bindingKdy);
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(arguments);
        Request request = new Request();
        request.setType(0x7);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待响应
        BasicReturns basicReturns = waitReturn(arguments.getRid());

        return basicReturns.isOk();
    }

    //  解除绑定
    public boolean queueUnbind(String exchangeName,String queueName) throws IOException {
        //  构造请求
        QueueUnbindArguments arguments = new QueueUnbindArguments();
        arguments.setExchangeName(exchangeName);
        arguments.setQueueName(queueName);
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(arguments);
        Request request = new Request();
        request.setType(0x8);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待响应
        BasicReturns basicReturns = waitReturn(arguments.getRid());

        return basicReturns.isOk();
    }

    //  发送消息
    public boolean basicPublish(String exchangeName, String routingKey, BasicProperties basicProperties,byte[] body) throws IOException {
        //  构造请求
        BasicPublishArguments arguments = new BasicPublishArguments();
        arguments.setExchangeName(exchangeName);
        arguments.setRoutingKey(routingKey);
        arguments.setBasicProperties(basicProperties);
        arguments.setBody(body);
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(arguments);
        Request request = new Request();
        request.setType(0x9);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待响应
        BasicReturns basicReturns = waitReturn(arguments.getRid());

        return basicReturns.isOk();
    }

    //  订阅消息
    public boolean basicConsume(String consumerTag,String queueName,boolean autoAck,Consume consume) throws MqException, IOException {
        //  先设置回调
        if(this.consume != null) {
            //  规定回调一个channel 只能有一个回调
            throw new MqException("该 channel 已经设置了回调，不能重复设置! channelId=" + channelId);
        }
        this.consume = consume;

        //  构造请求
        BasicConsumeArguments arguments = new BasicConsumeArguments();
        arguments.setConsumerTag(consumerTag);
        arguments.setQueueName(queueName);
        arguments.setAutoAck(autoAck);
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(arguments);
        Request request = new Request();
        request.setType(0xa);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待响应
        BasicReturns basicReturns = waitReturn(arguments.getRid());

        return basicReturns.isOk();
    }

    //  手动应答
    public boolean basicAck(String queueName,String messageId) throws IOException {
        //  构造请求
        BasicAckArguments arguments = new BasicAckArguments();
        arguments.setQueueName(queueName);
        arguments.setMessageId(messageId);
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(arguments);
        Request request = new Request();
        request.setType(0xb);
        request.setLength(payload.length);
        request.setPayload(payload);

        //  发送请求
        connection.writeRequest(request);

        //  等待响应
        BasicReturns basicReturns = waitReturn(arguments.getRid());

        return basicReturns.isOk();
    }

    //  等待并删除 响应过的响应
    private BasicReturns waitReturn(String rid) {
        BasicReturns basicReturns;
        //  不断从map中检查是否有对应的响应
        while((basicReturns = basicReturnsMap.get(rid)) == null) {
            //  未响应就阻塞等待
            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        //  响应之后释放内存
        basicReturnsMap.remove(rid);
        return basicReturns;
    }

    //  写入服务器给的响应，并唤醒所有线程
    public void putReturn(BasicReturns basicReturns) {
        synchronized (this) {
            basicReturnsMap.put(basicReturns.getRid(),basicReturns);
            notifyAll();
        }
    }

    private String generateRid() {
        return "R-" + UUID.randomUUID();
    }


    public String getChannelId() {
        return channelId;
    }
    public Consume getConsume() {
        return consume;
    }
}
